using System;
using System.Threading;
using System.Xml;

using GSN.Config;

using SKYPE4COMLib;

namespace GSN.Senders
{
    public class SkypeSMSSender : ISMSSender
    {
        private Skype skypeClass = null;
        private Exception lastError = null;
        private const int SKYPE_PROTOCOL_VERSION = 9;
        //private EventWaitHandle waitEvent = null;
        private bool isAttached = false;
        private int curMsgId = 0;

        #region ISMSSender Members

        public SkypeSMSSender()
        {
        }

        ~SkypeSMSSender()
        {
            /*if (waitEvent != null)
            {
                waitEvent.Close();
                waitEvent = null;
            }*/
            skypeClass = null;
        }

        public void load(XmlPasswordNode node)
        {
        }

        public void save(XmlPasswordWriter writer)
        {
        }

        void ISMSSender.start()
        {
            if (skypeClass == null)
            {
                skypeClass = new Skype();
                ((_ISkypeEvents_Event)skypeClass).SmsMessageStatusChanged += skypeClass_SmsMessageStatusChanged;
                ((_ISkypeEvents_Event)skypeClass).AttachmentStatus += SkypeSMSSender_AttachmentStatus;
                skypeClass.Timeout = 120 * 1000; // two minutes timeout, just in case
            }

            long timeCount = 0;

            if (!skypeClass.Client.IsRunning)
            {
                Logger.log("Skype Sender: Skype isn't running. Starting Skype...");

                skypeClass.Client.Start(true, true);

                // wait for load
                while (!skypeClass.Client.IsRunning)
                {
                    Thread.Sleep(2000);
                    timeCount += 2000;
                    if (timeCount == (30 * 1000))
                        MessageBoxTimeout.Show("GSN failed to start Skype. Please start Skype manually.", "GSN Notification", 30);

                    if (timeCount > (60 * 1000))
                    {
                        throw new Exception("Skype Sender: Skype Start Timeout");
                    }
                }

                // wait to load
                Thread.Sleep(2000);
            }

            if (!isAttached)
            {
                long tries = 0;
                while (!isAttached)
                {
                    skypeClass.Attach(SKYPE_PROTOCOL_VERSION, true);
                    if (isAttached)
                        break;
                    
                    if (tries == 5)
                        break;

                    if (!isAttached)
                    {
                        ++tries;
                        Logger.log("Skype Sender: Trying Attach Again...");
                    }
                }
                //waitEvent.WaitOne();
                if (!isAttached)
                {

                    throw new Exception("Skype Sender: Attached Failed!");
                }
            }

            /*if (waitEvent == null)
            {
                waitEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
            }*/
        }

        void SkypeSMSSender_AttachmentStatus(TAttachmentStatus Status)
        {
            Logger.log("Skype Sender: OnAttachmentStatus: {0}", skypeClass.Convert.AttachmentStatusToText(Status));

            switch (Status)
            {
                case TAttachmentStatus.apiAttachAvailable:
                    /*
                    try
                    {
                        skypeClass.Attach(SKYPE_PROTOCOL_VERSION, true);
                    }
                    catch (Exception ex)
                    {
                        Logger.log("Skype Sender: Waited Attached Exception - {0}", ex.Message);
                        isAttached = false;
                        //waitEvent.Set();
                    }*/
                    break;
                case TAttachmentStatus.apiAttachSuccess:
                    isAttached = true;
                    //waitEvent.Set();
                    break;
                case TAttachmentStatus.apiAttachNotAvailable:
                    //isAttached = false;
                    //waitEvent.Set();
                    break;
                case TAttachmentStatus.apiAttachPendingAuthorization:
                    //@@@
                    break;
                case TAttachmentStatus.apiAttachRefused:
                    break;
                case TAttachmentStatus.apiAttachUnknown:
                    break;
            }
        }

        void ISMSSender.stop()
        {
        }

        void ISMSSender.send(string recipient, string text)
        {
            // skype requires + at start of number
            if (!recipient.StartsWith("+"))
                recipient = "+" + recipient;

               /*
            if (skypeClass.AttachmentStatus != TAttachmentStatus.apiAttachAvailable)
                Thread.Sleep(3000);
            
            // attach gsn to Skype            
            Logger.log("Skype Sender: Wait for Skype to be attached...");
            try
            {
                skypeClass.Attach(5, true);
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Skype Sender Attach Exception: Inner: {0} Attach Status: {1}", ex.Message, skypeClass.Convert.AttachmentStatusToText(skypeClass.AttachmentStatus), ex);
            }
            // wait for attach
            Thread.Sleep(2000);

            timeCount = 0;
            while (skypeClass.AttachmentStatus != TAttachmentStatus.apiAttachSuccess)
            {
                switch (skypeClass.AttachmentStatus)
                {
                    case TAttachmentStatus.apiAttachAvailable:
                    case TAttachmentStatus.apiAttachUnknown:
                        // try to attach again
                        Logger.log("Skype Sender: Trying Attach Aagain - Status: {0}", skypeClass.Convert.AttachmentStatusToText(skypeClass.AttachmentStatus));
                        skypeClass.Attach(5, false);
                        break;
                    case TAttachmentStatus.apiAttachRefused:
                        throw new Exception("Skype Attachement Refused!");
                        break;
                    case TAttachmentStatus.apiAttachNotAvailable:
                        throw new Exception("Skype Attachement not Available!");
                        break;
                }

                Thread.Sleep(1000);
                timeCount += 1000;
                if (timeCount == (30 * 1000))
                    MessageBoxTimeout.Show("Please authorize GSN.exe to use Skype", "GSN Notification", 30);

                if (timeCount > (120 * 1000))
                {                    
                    throw new Exception("Skype Sender: Skype Attachement Time Out - Attachement Status: " + skypeClass.Convert.AttachmentStatusToText(skypeClass.AttachmentStatus));
                }
            }*/

            Logger.log("Skype Sender: Attached. Creating SMS Message...");
            
            lastError = null;
            SmsMessage msg = skypeClass.CreateSms(TSmsMessageType.smsMessageTypeOutgoing, recipient);
            curMsgId = msg.Id;
            msg.Body = text;
            msg.Send();

            Logger.log("Skype Sender: Waiting for Message to be Sent (ID: {0})...", msg.Id);
            
            long timeCount = 0;
            while (msg.Status != TSmsMessageStatus.smsMessageStatusDelivered)
            {
                Thread.Sleep(1000);
                timeCount += 1000;
                if (timeCount > (300 * 1000))
                {
                    if (lastError != null)
                    {
                        lastError = new Exception(string.Format("Skype Sender: Message (ID: {0}) Send Timeout. Status: {1}; Failure Reason: {2}",
                                msg.Id, skypeClass.Convert.SmsMessageStatusToText(msg.Status), msg.FailureReason.ToString()));
                    }
                }

                if (lastError != null)
                    break;
            }

            msg = null;
            curMsgId = 0;

            /*if (!sema.WaitOne(120 * 1000, false))
            {
                throw new Exception("Skype Send Sms Timeout Error");
            }*/

            if (lastError != null)
                throw lastError;
            
            Logger.log("Skype Sender: SMS Sent.");
        }

        #endregion

        private void skypeClass_SmsMessageStatusChanged(SmsMessage pMessage, TSmsMessageStatus Status)
        {
            Logger.log("Skype Sender: SMS Message (ID: {0}) Status Changed to {1}",
                pMessage.Id, skypeClass.Convert.SmsMessageStatusToText(Status));

            if (pMessage.Id == curMsgId)
            {
                switch (Status)
                {
                    case TSmsMessageStatus.smsMessageStatusFailed:
                        Logger.log("Skype Sender: SMS Message (ID: {0}) Failed. Reason: {1}", pMessage.Id, pMessage.FailureReason.ToString());
                        lastError = new Exception("Skype Send Failed (ID:" + pMessage.Id + "): " + pMessage.FailureReason);
                        //sema.Set();
                        break;
                    case TSmsMessageStatus.smsMessageStatusReceived:
                        //sema.Set();
                        break;
                }
            }            
        }

        public override bool Equals(object obj)
        {
            if (obj is ISMSSender)
            {
                if (obj is SkypeSMSSender)
                {
                    //SkypeSMSSender s = (SkypeSMSSender)obj;
                    return true;
                }
            }

            return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return "Skype";
        }

        public string getConfiguratorTypeName()
        {
            return "GSNConf.Senders.SkypeSMSSenderConfigurator";
        }

        public bool showConfigDialog(IConfigurator configurator)
        {
            configurator.show();
            return true;
        }


    }
}
